package cn.xyfmzmy.eventloop;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.PriorityBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

public class MzmyEventLoop implements EventLoop{

    // 线程编号
    private static AtomicInteger THREAD_NAME_INDEX = new AtomicInteger(0);

    // 唤醒线程的Runnable
    private final Runnable WAKE_UP = () -> {};

    // 存储任务的队列
    private final BlockingQueue<Runnable> taskQueue;

    // 存储定时任务的队列
    private final PriorityBlockingQueue<ScheduleTask> scheduleTaskQueue;

    // 执行线程
    private final EventLoopThread eventLoopThread;

    public MzmyEventLoop() {
        this.taskQueue = new ArrayBlockingQueue<>(1024);
        this.scheduleTaskQueue = new PriorityBlockingQueue<>(1024);
        this.eventLoopThread = new EventLoopThread("mzmy-eventloop-thread-" + THREAD_NAME_INDEX.incrementAndGet());
        this.eventLoopThread.start();
    }

    public PriorityBlockingQueue<ScheduleTask> getScheduleTaskQueue() {
        return scheduleTaskQueue;
    }

    @Override
    public void execute(Runnable runnable) {
        if (!taskQueue.offer(runnable)) {
            throw new RuntimeException("任务队列已满");
        }
    }

    // 延迟执行1次
    @Override
    public void schedule(Runnable task, long delay, TimeUnit unit) {
        ScheduleTask scheduleTask = new ScheduleTask(task, delayMs(delay,unit), -1, this);
        if (!scheduleTaskQueue.offer(scheduleTask)) {
            throw new RuntimeException("任务队列已满");
        }
        execute(WAKE_UP);
    }

    // 每隔一段时间定时执行
    @Override
    public void scheduleAtFixedRate(Runnable task, long initialDelay, long period, TimeUnit unit) {
        ScheduleTask scheduleTask = new ScheduleTask(task, delayMs(initialDelay,unit), unit.toMillis(period), this);
        if (!scheduleTaskQueue.offer(scheduleTask)) {
            throw new RuntimeException("任务队列已满");
        }
        execute(WAKE_UP);
    }

    private long delayMs(long delay, TimeUnit unit){
        return System.currentTimeMillis() + unit.toMillis(delay);
    }

    @Override
    public EventLoop next() {
        return this;
    }

    private Runnable getTask(){
        // 先查看是否有定时任务需要执行
        ScheduleTask scheduleTask = scheduleTaskQueue.peek();
        if(scheduleTask == null){
            // 没有定时任务需要执行，获取普通任务
            try {
                Runnable task = taskQueue.take();
                // 这个地方是阻塞的，我们希望有新任务被添加时能够唤醒线程(添加普通任务时会直接返回并唤醒，无需处理)
                // 因此在添加定时任务时会添加一个WAKE_UP任务，拿到WAKE_UP任务时直接返回null，进入下一轮任务获取
                if(task == WAKE_UP){
                    return null;
                }
                return task;
            } catch (InterruptedException e) {
                return null;
            }
        }
        if (scheduleTask.getDeadline() <= System.currentTimeMillis()){
            // deadline到了，执行定时任务
            return scheduleTaskQueue.poll();
        }
        // deadline未到，执行普通任务
        try {
            // 在超时时间内尝试获取任务，如果在超时时间内队列中有任务，则立即返回该任务，否则返回null
            Runnable task = taskQueue.poll(scheduleTask.getDeadline() - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
            // 这个地方是阻塞的，我们希望有新任务被添加时能够唤醒线程(添加普通任务时会直接返回并唤醒，无需处理)
            // 因此在添加定时任务时会添加一个WAKE_UP任务，拿到WAKE_UP任务时直接返回null，进入下一轮任务获取
            if( task == WAKE_UP ){
                return null;
            }
            return task;
        } catch (InterruptedException e) {
            return null;
        }
    }

    // 定义执行线程
    class EventLoopThread extends Thread{

        public EventLoopThread(String name) {
            super(name);
        }

        @Override
        public void run() {
            // 消费队列中的任务
            while (true){
                Runnable task = getTask();
                if(task != null){
                    task.run();
                }
            }
        }
    }
}
